Automatically translate reviewed extensions and their actions and conditions (#7478)

This commit is contained in:
Clément Pasteau
2025-03-21 15:09:06 +01:00
committed by GitHub
parent 495b99356d
commit ae09ad50ec
82 changed files with 891 additions and 299 deletions
@@ -0,0 +1,47 @@
# GitHub Action to update extension translations.
# It copies the latest messages.js files from the GDevelop-extensions repository
# and opens a Pull Request with the changes on GDevelop's repository.
name: Update extension translations
on:
push:
branches:
- master
tags-ignore:
- "**" # Don't run on new tags
workflow_dispatch: # Allows manual triggering from the Actions tab
jobs:
update-extension-translations:
runs-on: ubuntu-latest
steps:
- name: Checkout current repository
uses: actions/checkout@v3
- name: Clone GDevelop-extensions repository
run: git clone https://github.com/GDevelopApp/GDevelop-extensions.git /tmp/GDevelop-extensions
- name: Copy and rename translation files
run: |
mkdir -p newIDE/app/src/locales
for folder in /tmp/GDevelop-extensions/.translations/*; do
if [ -d "$folder" ]; then
lang=$(basename "$folder")
mkdir -p "newIDE/app/src/locales/$lang"
cp "$folder/messages.js" "newIDE/app/src/locales/$lang/extension-messages.js"
fi
done
cp /tmp/GDevelop-extensions/.translations/LocalesMetadata.js newIDE/app/src/locales/ExtensionLocalesMetadata.js
- name: Create Pull Request with updated translations
uses: peter-evans/create-pull-request@v6
with:
commit-message: Update extension translations [skip ci]
branch: chore/update-extension-translations
delete-branch: true
title: "[Auto PR] Update extension translations"
body: |
This updates the extension translations by copying the latest messages.js files from the GDevelop-extensions repository.
Each messages.js file is renamed to extension-messages.js and placed in the corresponding language folder under `newIDE/app/src/locales`.
Please review the changes carefully before merging.
+40 -70
View File
@@ -18,8 +18,8 @@
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/PlatformManager.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/BehaviorsSharedData.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Tools/Localization.h"
namespace gd {
@@ -200,11 +200,11 @@ PlatformExtension::AddExpressionAndConditionAndAction(
group,
icon)
: AddStrExpression(name,
fullname,
expressionDescriptionTemplate.FindAndReplace(
"<subject>", descriptionSubject),
group,
icon);
fullname,
expressionDescriptionTemplate.FindAndReplace(
"<subject>", descriptionSubject),
group,
icon);
return MultipleInstructionMetadata::WithExpressionAndConditionAndAction(
expression, condition, action);
@@ -244,12 +244,10 @@ gd::ObjectMetadata& PlatformExtension::AddEventsBasedObject(
const gd::String& description,
const gd::String& icon24x24) {
gd::String nameWithNamespace = GetNameSpace() + name;
objectsInfos[nameWithNamespace] = ObjectMetadata(GetNameSpace(),
nameWithNamespace,
fullname,
description,
icon24x24)
.SetHelpPath(GetHelpPath());
objectsInfos[nameWithNamespace] =
ObjectMetadata(
GetNameSpace(), nameWithNamespace, fullname, description, icon24x24)
.SetHelpPath(GetHelpPath());
return objectsInfos[nameWithNamespace];
}
@@ -353,8 +351,7 @@ gd::BehaviorMetadata& PlatformExtension::GetBehaviorMetadata(
return badBehaviorMetadata;
}
bool PlatformExtension::HasBehavior(
const gd::String& behaviorType) const {
bool PlatformExtension::HasBehavior(const gd::String& behaviorType) const {
return behaviorsInfo.find(behaviorType) != behaviorsInfo.end();
}
@@ -386,7 +383,7 @@ gd::InstructionMetadata& PlatformExtension::AddDuplicatedAction(
auto copiedAction = actionsInfos.find(copiedNameWithNamespace);
if (copiedAction == actionsInfos.end()) {
gd::LogError("Could not find an action with name " +
copiedNameWithNamespace + " to copy.");
copiedNameWithNamespace + " to copy.");
} else {
actionsInfos[newNameWithNamespace] = copiedAction->second;
}
@@ -406,7 +403,7 @@ gd::InstructionMetadata& PlatformExtension::AddDuplicatedCondition(
auto copiedCondition = conditionsInfos.find(copiedNameWithNamespace);
if (copiedCondition == conditionsInfos.end()) {
gd::LogError("Could not find a condition with name " +
copiedNameWithNamespace + " to copy.");
copiedNameWithNamespace + " to copy.");
} else {
conditionsInfos[newNameWithNamespace] = copiedCondition->second;
}
@@ -423,7 +420,7 @@ gd::ExpressionMetadata& PlatformExtension::AddDuplicatedExpression(
auto copiedExpression = expressionsInfos.find(copiedNameWithNamespace);
if (copiedExpression == expressionsInfos.end()) {
gd::LogError("Could not find an expression with name " +
copiedNameWithNamespace + " to copy.");
copiedNameWithNamespace + " to copy.");
} else {
expressionsInfos[newNameWithNamespace] = copiedExpression->second;
}
@@ -440,7 +437,7 @@ gd::ExpressionMetadata& PlatformExtension::AddDuplicatedStrExpression(
auto copiedExpression = strExpressionsInfos.find(copiedNameWithNamespace);
if (copiedExpression == strExpressionsInfos.end()) {
gd::LogError("Could not find a string expression with name " +
copiedNameWithNamespace + " to copy.");
copiedNameWithNamespace + " to copy.");
} else {
strExpressionsInfos[newNameWithNamespace] = copiedExpression->second;
}
@@ -468,7 +465,8 @@ PlatformExtension::GetAllStrExpressions() {
return strExpressionsInfos;
}
const std::vector<gd::DependencyMetadata>& PlatformExtension::GetAllDependencies() const {
const std::vector<gd::DependencyMetadata>&
PlatformExtension::GetAllDependencies() const {
return extensionDependenciesMetadata;
}
@@ -476,7 +474,8 @@ std::vector<gd::DependencyMetadata>& PlatformExtension::GetAllDependencies() {
return extensionDependenciesMetadata;
}
const std::vector<gd::SourceFileMetadata>& PlatformExtension::GetAllSourceFiles() const {
const std::vector<gd::SourceFileMetadata>&
PlatformExtension::GetAllSourceFiles() const {
return extensionSourceFilesMetadata;
}
@@ -615,37 +614,6 @@ void PlatformExtension::SetNameSpace(gd::String nameSpace_) {
nameSpace = nameSpace_ + GetNamespaceSeparator();
}
std::vector<gd::String> PlatformExtension::GetBuiltinExtensionsNames() {
std::vector<gd::String> builtinExtensions;
builtinExtensions.push_back("Sprite");
builtinExtensions.push_back("BuiltinObject");
builtinExtensions.push_back("BuiltinAudio");
builtinExtensions.push_back("BuiltinMouse");
builtinExtensions.push_back("BuiltinKeyboard");
builtinExtensions.push_back("BuiltinJoystick");
builtinExtensions.push_back("BuiltinTime");
builtinExtensions.push_back("BuiltinFile");
builtinExtensions.push_back("BuiltinVariables");
builtinExtensions.push_back("BuiltinCamera");
builtinExtensions.push_back("BuiltinWindow");
builtinExtensions.push_back("BuiltinNetwork");
builtinExtensions.push_back("BuiltinScene");
builtinExtensions.push_back("BuiltinAdvanced");
builtinExtensions.push_back("BuiltinCommonConversions");
builtinExtensions.push_back("BuiltinStringInstructions");
builtinExtensions.push_back("BuiltinMathematicalTools");
builtinExtensions.push_back("BuiltinExternalLayouts");
builtinExtensions.push_back("BuiltinCommonInstructions");
return builtinExtensions;
}
bool PlatformExtension::IsBuiltin() const {
std::vector<gd::String> builtinExtensions = GetBuiltinExtensionsNames();
return std::find(builtinExtensions.begin(), builtinExtensions.end(), name) !=
builtinExtensions.end();
}
void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
for (std::map<gd::String, gd::InstructionMetadata>::iterator it =
GetAllActions().begin();
@@ -791,41 +759,42 @@ void PlatformExtension::StripUnimplementedInstructionsAndExpressions() {
}
}
gd::String
PlatformExtension::GetEventsFunctionFullType(const gd::String &extensionName,
const gd::String &functionName) {
const auto &separator = GetNamespaceSeparator();
gd::String PlatformExtension::GetEventsFunctionFullType(
const gd::String& extensionName, const gd::String& functionName) {
const auto& separator = GetNamespaceSeparator();
return extensionName + separator + functionName;
}
gd::String PlatformExtension::GetBehaviorEventsFunctionFullType(
const gd::String &extensionName, const gd::String &behaviorName,
const gd::String &functionName) {
const auto &separator = GetNamespaceSeparator();
const gd::String& extensionName,
const gd::String& behaviorName,
const gd::String& functionName) {
const auto& separator = GetNamespaceSeparator();
return extensionName + separator + behaviorName + separator + functionName;
}
gd::String
PlatformExtension::GetBehaviorFullType(const gd::String &extensionName,
const gd::String &behaviorName) {
const auto &separator = GetNamespaceSeparator();
gd::String PlatformExtension::GetBehaviorFullType(
const gd::String& extensionName, const gd::String& behaviorName) {
const auto& separator = GetNamespaceSeparator();
return extensionName + separator + behaviorName;
}
gd::String PlatformExtension::GetObjectEventsFunctionFullType(
const gd::String &extensionName, const gd::String &objectName,
const gd::String &functionName) {
const auto &separator = GetNamespaceSeparator();
const gd::String& extensionName,
const gd::String& objectName,
const gd::String& functionName) {
const auto& separator = GetNamespaceSeparator();
return extensionName + separator + objectName + separator + functionName;
}
gd::String PlatformExtension::GetObjectFullType(const gd::String &extensionName,
const gd::String &objectName) {
const auto &separator = GetNamespaceSeparator();
gd::String PlatformExtension::GetObjectFullType(const gd::String& extensionName,
const gd::String& objectName) {
const auto& separator = GetNamespaceSeparator();
return extensionName + separator + objectName;
}
gd::String PlatformExtension::GetExtensionFromFullObjectType(const gd::String& type) {
gd::String PlatformExtension::GetExtensionFromFullObjectType(
const gd::String& type) {
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
if (separatorIndex == std::string::npos) {
@@ -834,7 +803,8 @@ gd::String PlatformExtension::GetExtensionFromFullObjectType(const gd::String& t
return type.substr(0, separatorIndex);
}
gd::String PlatformExtension::GetObjectNameFromFullObjectType(const gd::String& type) {
gd::String PlatformExtension::GetObjectNameFromFullObjectType(
const gd::String& type) {
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
if (separatorIndex == std::string::npos) {
+1 -13
View File
@@ -13,11 +13,11 @@
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/Extensions/Metadata/EventMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionOrExpressionGroupMetadata.h"
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/String.h"
#include "GDCore/Tools/VersionPriv.h"
@@ -440,12 +440,6 @@ class GD_CORE_API PlatformExtension {
*/
bool IsDeprecated() const { return deprecated; }
/**
* \brief Return true if the extension is a standard extension that cannot be
* deactivated
*/
bool IsBuiltin() const;
/**
* \brief Get the namespace of the extension.
* \note The namespace is simply the name of the extension concatenated with
@@ -640,12 +634,6 @@ class GD_CORE_API PlatformExtension {
}
///@}
/**
* \brief Return the name of all the extensions which are considered provided
* by platforms.
*/
static std::vector<gd::String> GetBuiltinExtensionsNames();
/**
* \brief Get the string used to separate the name of the
* instruction/expression and the extension.
+6
View File
@@ -6,6 +6,7 @@
#if defined(EMSCRIPTEN)
#include <emscripten.h>
#include "GDCore/String.h"
namespace gd {
@@ -31,5 +32,10 @@ gd::String GetTranslation(const char* str) { // TODO: Inline?
str);
return gd::String(translatedStr); // TODO: Is copying necessary?
}
gd::String GetTranslation(const gd::String& str) {
return GetTranslation(str.c_str());
}
} // namespace gd
#endif
+5 -3
View File
@@ -8,10 +8,10 @@
/** @file
* Provide a way to mark strings to be translated.
*
*
* Strings to be translated in GDevelop Core codebase
* are marked with the underscore macro, for example: _("Hello World").
*
*
* The macro is then defined to be using the translation function
* of the underlying platform (Emscripten for GDevelop 5).
*/
@@ -26,8 +26,10 @@
#endif
namespace gd {
gd::String GetTranslation(const gd::String& str);
gd::String GetTranslation(const char* str);
}
} // namespace gd
#define _(s) gd::GetTranslation(u8##s)
@@ -31,7 +31,7 @@ namespace gdjs {
void MetadataDeclarationHelper::DeclareExtension(
gd::PlatformExtension &extension,
const gd::EventsFunctionsExtension &eventsFunctionsExtension) {
gd::String fullName = eventsFunctionsExtension.GetFullName() ||
gd::String fullName = GetTranslation(eventsFunctionsExtension.GetFullName()) ||
eventsFunctionsExtension.GetName();
extension
.SetExtensionInformation(eventsFunctionsExtension.GetName(), fullName,
@@ -92,10 +92,11 @@ gd::BehaviorMetadata &MetadataDeclarationHelper::DeclareBehaviorMetadata(
extension
.AddBehavior(
eventsBasedBehavior.GetName(),
eventsBasedBehavior.GetFullName() ||
GetTranslation(eventsBasedBehavior.GetFullName()) ||
eventsBasedBehavior.GetName(),
eventsBasedBehavior.GetName(),
eventsBasedBehavior.GetDescription(), "",
GetTranslation(eventsBasedBehavior.GetDescription()),
"",
GetExtensionIconUrl(extension), "",
std::make_shared<gd::CustomBehavior>(
eventsBasedBehavior.GetName(), project,
@@ -124,9 +125,9 @@ gd::ObjectMetadata &MetadataDeclarationHelper::DeclareObjectMetadata(
auto &objectMetadata =
extension
.AddEventsBasedObject(eventsBasedObject.GetName(),
eventsBasedObject.GetFullName() ||
GetTranslation(eventsBasedObject.GetFullName()) ||
eventsBasedObject.GetName(),
eventsBasedObject.GetDescription(),
GetTranslation(eventsBasedObject.GetDescription()),
GetExtensionIconUrl(extension))
// TODO Change the metadata model to only set a category on the
// extension. If an extension has behavior or object across
@@ -456,8 +457,8 @@ MetadataDeclarationHelper::DeclareInstructionOrExpressionMetadata(
}
}
const gd::String &MetadataDeclarationHelper::GetFullName(const gd::EventsFunction &eventsFunction) {
return eventsFunction.GetFullName() || eventsFunction.GetName();
gd::String MetadataDeclarationHelper::GetFullName(const gd::EventsFunction &eventsFunction) {
return GetTranslation(eventsFunction.GetFullName()) || eventsFunction.GetName();
};
gd::String MetadataDeclarationHelper::GetDefaultSentence(
@@ -484,27 +485,27 @@ gd::String MetadataDeclarationHelper::GetDefaultSentence(
};
gd::String MetadataDeclarationHelper::GetFreeFunctionSentence(const gd::EventsFunction &eventsFunction) {
return eventsFunction.GetSentence().empty()
return GetTranslation(eventsFunction.GetSentence()).empty()
? GetDefaultSentence(eventsFunction, 0, 1)
: eventsFunction.GetSentence();
: GetTranslation(eventsFunction.GetSentence());
};
gd::String MetadataDeclarationHelper::GetBehaviorFunctionSentence(
const gd::EventsFunction &eventsFunction,
const bool excludeObjectParameter) {
return eventsFunction.GetSentence().empty()
return GetTranslation(eventsFunction.GetSentence()).empty()
? GetDefaultSentence(eventsFunction,
excludeObjectParameter ? 2 : 0, 0)
: eventsFunction.GetSentence();
: GetTranslation(eventsFunction.GetSentence());
};
gd::String MetadataDeclarationHelper::GetObjectFunctionSentence(
const gd::EventsFunction &eventsFunction,
const bool excludeObjectParameter) {
return eventsFunction.GetSentence().empty()
return GetTranslation(eventsFunction.GetSentence()).empty()
? GetDefaultSentence(eventsFunction,
excludeObjectParameter ? 1 : 0, 0)
: eventsFunction.GetSentence();
: GetTranslation(eventsFunction.GetSentence());
};
/**
@@ -572,7 +573,7 @@ gd::InstructionMetadata &MetadataDeclarationHelper::DeclareInstructionMetadata(
auto &condition = extension.AddCondition(
eventsFunction.GetName(),
GetFullName(eventsFunction),
eventsFunction.GetDescription() || GetFullName(eventsFunction),
GetTranslation(eventsFunction.GetDescription()) || GetFullName(eventsFunction),
GetFreeFunctionSentence(eventsFunction), eventsFunction.GetGroup(),
GetExtensionIconUrl(extension), GetExtensionIconUrl(extension));
// By convention, first parameter is always the Runtime Scene.
@@ -625,7 +626,7 @@ gd::InstructionMetadata &MetadataDeclarationHelper::DeclareInstructionMetadata(
auto &action = extension.AddAction(
eventsFunction.GetName(),
GetFullName(eventsFunction),
eventsFunction.GetDescription() || GetFullName(eventsFunction),
GetTranslation(eventsFunction.GetDescription()) || GetFullName(eventsFunction),
GetFreeFunctionSentence(eventsFunction), eventsFunction.GetGroup(),
GetExtensionIconUrl(extension), GetExtensionIconUrl(extension));
// By convention, first parameter is always the Runtime Scene.
@@ -1345,7 +1346,7 @@ void MetadataDeclarationHelper::AddParameter(
if (!parameter.IsCodeOnly()) {
instructionOrExpression
.AddParameter(parameter.GetType(),
parameter.GetDescription() || parameter.GetName(),
GetTranslation(parameter.GetDescription()) || parameter.GetName(),
"", // See below for adding the extra information
parameter.IsOptional())
// Manually add the "extra info" without relying on addParameter (or
@@ -1354,7 +1355,7 @@ void MetadataDeclarationHelper::AddParameter(
// declarations when dealing with object).
.SetParameterExtraInfo(parameter.GetExtraInfo());
instructionOrExpression.SetParameterLongDescription(
parameter.GetLongDescription());
GetTranslation(parameter.GetLongDescription()));
instructionOrExpression.SetDefaultValue(parameter.GetDefaultValue());
} else {
instructionOrExpression.AddCodeOnlyParameter(parameter.GetType(),
@@ -329,8 +329,7 @@ private:
static gd::String UncapitalizeFirstLetter(const gd::String &string);
static const gd::String &
GetFullName(const gd::EventsFunction &eventsFunction);
static gd::String GetFullName(const gd::EventsFunction &eventsFunction);
static gd::String GetDefaultSentence(const gd::EventsFunction &eventsFunction,
const int firstParameterIndex,
const int parameterIndexOffset);
-1
View File
@@ -2238,7 +2238,6 @@ interface PlatformExtension {
[Const, Ref] DOMString GetLicense();
[Const, Ref] DOMString GetHelpPath();
[Const, Ref] DOMString GetIconUrl();
boolean IsBuiltin();
[Const, Ref] DOMString GetNameSpace();
[Ref] InstructionMetadata AddDuplicatedAction(
-1
View File
@@ -1701,7 +1701,6 @@ export class PlatformExtension extends EmscriptenObject {
getLicense(): string;
getHelpPath(): string;
getIconUrl(): string;
isBuiltin(): boolean;
getNameSpace(): string;
addDuplicatedAction(newActionName: string, copiedActionName: string): InstructionMetadata;
addDuplicatedCondition(newConditionName: string, copiedConditionName: string): InstructionMetadata;
-1
View File
@@ -28,7 +28,6 @@ declare class gdPlatformExtension {
getLicense(): string;
getHelpPath(): string;
getIconUrl(): string;
isBuiltin(): boolean;
getNameSpace(): string;
addDuplicatedAction(newActionName: string, copiedActionName: string): gdInstructionMetadata;
addDuplicatedCondition(newConditionName: string, copiedConditionName: string): gdInstructionMetadata;
@@ -1,5 +1,6 @@
// @flow
import * as React from 'react';
import { type I18n as I18nType } from '@lingui/core';
import { type FiltersState, useFilters } from '../../UI/Search/FiltersChooser';
import {
getBehaviorsRegistry,
@@ -51,7 +52,7 @@ type BehaviorStoreState = {|
setInstalledBehaviorMetadataList: (
installedBehaviorMetadataList: Array<SearchableBehaviorMetadata>
) => void,
behaviorShortHeadersByType: { [name: string]: BehaviorShortHeader },
translatedBehaviorShortHeadersByType: { [name: string]: BehaviorShortHeader },
filtersState: FiltersState,
|};
@@ -67,7 +68,7 @@ export const BehaviorStoreContext = React.createContext<BehaviorStoreState>({
chosenCategory: '',
setChosenCategory: () => {},
setInstalledBehaviorMetadataList: () => {},
behaviorShortHeadersByType: {},
translatedBehaviorShortHeadersByType: {},
filtersState: {
chosenFilters: new Set(),
addFilter: () => {},
@@ -79,11 +80,13 @@ export const BehaviorStoreContext = React.createContext<BehaviorStoreState>({
type BehaviorStoreStateProviderProps = {|
children: React.Node,
i18n: I18nType,
defaultSearchText?: string,
|};
export const BehaviorStoreStateProvider = ({
children,
i18n,
defaultSearchText,
}: BehaviorStoreStateProviderProps) => {
const [
@@ -91,17 +94,18 @@ export const BehaviorStoreStateProvider = ({
setInstalledBehaviorMetadataList,
] = React.useState<Array<SearchableBehaviorMetadata>>([]);
const [
behaviorShortHeadersByType,
setBehaviorShortHeadersByType,
translatedBehaviorShortHeadersByType,
setTranslatedBehaviorShortHeadersByType,
] = React.useState<{
[string]: BehaviorShortHeader,
}>({});
const preferences = React.useContext(PreferencesContext);
const { showCommunityExtensions } = preferences.values;
const { showCommunityExtensions, language } = preferences.values;
const [firstBehaviorIds, setFirstBehaviorIds] = React.useState<Array<string>>(
[]
);
const [loadedLanguage, setLoadedLanguage] = React.useState<?string>(null);
const [error, setError] = React.useState<?Error>(null);
const isLoading = React.useRef<boolean>(false);
@@ -115,20 +119,34 @@ export const BehaviorStoreStateProvider = ({
() => {
// Don't attempt to load again resources and filters if they
// were loaded already.
if (Object.keys(behaviorShortHeadersByType).length || isLoading.current)
if (
(Object.keys(translatedBehaviorShortHeadersByType).length &&
loadedLanguage === language) ||
isLoading.current
)
return;
(async () => {
setError(null);
// Reset the search text to avoid showing the previous search results
// in case they were on a different language.
setSearchText(emptySearchText);
isLoading.current = true;
try {
const behaviorsRegistry: BehaviorsRegistry = await getBehaviorsRegistry();
const behaviorShortHeaders = behaviorsRegistry.headers;
const behaviorShortHeadersByType = {};
behaviorShortHeaders.forEach(behavior => {
behaviorShortHeadersByType[behavior.type] = behavior;
const translatedBehaviorShortHeadersByType = {};
behaviorShortHeaders.forEach(behaviorShortHeader => {
const translatedBehaviorShortHeader: BehaviorShortHeader = {
...behaviorShortHeader,
fullName: i18n._(behaviorShortHeader.fullName),
description: i18n._(behaviorShortHeader.description),
};
translatedBehaviorShortHeadersByType[
behaviorShortHeader.type
] = translatedBehaviorShortHeader;
});
console.info(
@@ -136,7 +154,10 @@ export const BehaviorStoreStateProvider = ({
behaviorShortHeaders ? behaviorShortHeaders.length : 0
} behaviors from the extension store.`
);
setBehaviorShortHeadersByType(behaviorShortHeadersByType);
setTranslatedBehaviorShortHeadersByType(
translatedBehaviorShortHeadersByType
);
setLoadedLanguage(language);
setFirstBehaviorIds(
behaviorsRegistry.views.default.firstIds.map(
({ extensionName, behaviorName }) =>
@@ -157,14 +178,24 @@ export const BehaviorStoreStateProvider = ({
isLoading.current = false;
})();
},
[behaviorShortHeadersByType, isLoading]
[
translatedBehaviorShortHeadersByType,
isLoading,
i18n,
language,
loadedLanguage,
]
);
React.useEffect(
() => {
// Don't attempt to load again extensions and filters if they
// were loaded already.
if (Object.keys(behaviorShortHeadersByType).length || isLoading.current)
if (
(Object.keys(translatedBehaviorShortHeadersByType).length &&
loadedLanguage === language) ||
isLoading.current
)
return;
const timeoutId = setTimeout(() => {
@@ -173,46 +204,53 @@ export const BehaviorStoreStateProvider = ({
}, BEHAVIORS_FETCH_TIMEOUT);
return () => clearTimeout(timeoutId);
},
[fetchBehaviors, behaviorShortHeadersByType, isLoading]
[
fetchBehaviors,
translatedBehaviorShortHeadersByType,
isLoading,
language,
loadedLanguage,
]
);
const allBehaviors = React.useMemo(
const allTranslatedBehaviors = React.useMemo(
() => {
const allBehaviors: {
const allTranslatedBehaviors: {
[name: string]: BehaviorShortHeader | SearchableBehaviorMetadata,
} = {};
for (const type in behaviorShortHeadersByType) {
allBehaviors[type] = behaviorShortHeadersByType[type];
for (const type in translatedBehaviorShortHeadersByType) {
allTranslatedBehaviors[type] =
translatedBehaviorShortHeadersByType[type];
}
for (const installedBehaviorMetadata of installedBehaviorMetadataList) {
allBehaviors[
allTranslatedBehaviors[
installedBehaviorMetadata.type
] = installedBehaviorMetadata;
}
return allBehaviors;
return allTranslatedBehaviors;
},
[installedBehaviorMetadataList, behaviorShortHeadersByType]
[installedBehaviorMetadataList, translatedBehaviorShortHeadersByType]
);
const allCategories = React.useMemo(
() => {
const categoriesSet = new Set();
for (const type in allBehaviors) {
categoriesSet.add(allBehaviors[type].category);
for (const type in allTranslatedBehaviors) {
categoriesSet.add(allTranslatedBehaviors[type].category);
}
const sortedCategories = [...categoriesSet].sort((tag1, tag2) =>
tag1.toLowerCase().localeCompare(tag2.toLowerCase())
);
return sortedCategories;
},
[allBehaviors]
[allTranslatedBehaviors]
);
const filters = React.useMemo(
() => {
const tagsSet = new Set();
for (const type in allBehaviors) {
const behavior = allBehaviors[type];
for (const type in allTranslatedBehaviors) {
const behavior = allTranslatedBehaviors[type];
behavior.tags.forEach(tag => {
if (
showCommunityExtensions ||
@@ -232,7 +270,7 @@ export const BehaviorStoreStateProvider = ({
tagsTree: [],
};
},
[allBehaviors, showCommunityExtensions]
[allTranslatedBehaviors, showCommunityExtensions]
);
const defaultFirstSearchItemIds = React.useMemo(
@@ -245,7 +283,7 @@ export const BehaviorStoreStateProvider = ({
const searchResults: ?Array<
SearchResult<BehaviorShortHeader | SearchableBehaviorMetadata>
> = useSearchStructuredItem(allBehaviors, {
> = useSearchStructuredItem(allTranslatedBehaviors, {
searchText,
chosenItemCategory: chosenCategory,
chosenCategory: filtersState.chosenCategory,
@@ -268,7 +306,7 @@ export const BehaviorStoreStateProvider = ({
searchText,
setSearchText,
setInstalledBehaviorMetadataList,
behaviorShortHeadersByType,
translatedBehaviorShortHeadersByType,
filtersState,
}),
[
@@ -280,7 +318,7 @@ export const BehaviorStoreStateProvider = ({
setChosenCategory,
searchText,
setInstalledBehaviorMetadataList,
behaviorShortHeadersByType,
translatedBehaviorShortHeadersByType,
filtersState,
fetchBehaviors,
]
@@ -1,5 +1,6 @@
// @flow
import * as React from 'react';
import { type I18n as I18nType } from '@lingui/core';
import { type FiltersState, useFilters } from '../../UI/Search/FiltersChooser';
import {
getExtensionsRegistry,
@@ -29,7 +30,9 @@ type ExtensionStoreState = {|
allCategories: string[],
chosenCategory: string,
setChosenCategory: string => void,
extensionShortHeadersByName: { [name: string]: ExtensionShortHeader },
translatedExtensionShortHeadersByName: {
[name: string]: ExtensionShortHeader,
},
filtersState: FiltersState,
hasExtensionNamed: (extensionName: string) => boolean,
|};
@@ -45,7 +48,7 @@ export const ExtensionStoreContext = React.createContext<ExtensionStoreState>({
// '' means all categories.
chosenCategory: '',
setChosenCategory: () => {},
extensionShortHeadersByName: {},
translatedExtensionShortHeadersByName: {},
filtersState: {
chosenFilters: new Set(),
addFilter: () => {},
@@ -58,24 +61,27 @@ export const ExtensionStoreContext = React.createContext<ExtensionStoreState>({
type ExtensionStoreStateProviderProps = {|
children: React.Node,
i18n: I18nType,
defaultSearchText?: string,
|};
export const ExtensionStoreStateProvider = ({
children,
i18n,
defaultSearchText,
}: ExtensionStoreStateProviderProps) => {
const [
extensionShortHeadersByName,
setExtensionShortHeadersByName,
translatedExtensionShortHeadersByName,
setTranslatedExtensionShortHeadersByName,
] = React.useState<{
[string]: ExtensionShortHeader,
}>({});
const preferences = React.useContext(PreferencesContext);
const { showCommunityExtensions } = preferences.values;
const { showCommunityExtensions, language } = preferences.values;
const [firstExtensionIds, setFirstExtensionIds] = React.useState<
Array<string>
>([]);
const [loadedLanguage, setLoadedLanguage] = React.useState<?string>(null);
const [error, setError] = React.useState<?Error>(null);
const isLoading = React.useRef<boolean>(false);
@@ -88,21 +94,35 @@ export const ExtensionStoreStateProvider = ({
const fetchExtensionsAndFilters = React.useCallback(
() => {
// Don't attempt to load again resources and filters if they
// were loaded already.
if (Object.keys(extensionShortHeadersByName).length || isLoading.current)
// are loading or were loaded already in the current language.
if (
(Object.keys(translatedExtensionShortHeadersByName).length &&
loadedLanguage === language) ||
isLoading.current
)
return;
(async () => {
setError(null);
// Reset the search text to avoid showing the previous search results
// in case they were on a different language.
setSearchText(emptySearchText);
isLoading.current = true;
try {
const extensionRegistry: ExtensionsRegistry = await getExtensionsRegistry();
const { headers } = extensionRegistry;
const extensionShortHeadersByName = {};
headers.forEach(extension => {
extensionShortHeadersByName[extension.name] = extension;
const translatedExtensionShortHeadersByName = {};
headers.forEach(extensionShortHeader => {
const translatedExtensionShortHeader: ExtensionShortHeader = {
...extensionShortHeader,
fullName: i18n._(extensionShortHeader.fullName),
shortDescription: i18n._(extensionShortHeader.shortDescription),
};
translatedExtensionShortHeadersByName[
extensionShortHeader.name
] = translatedExtensionShortHeader;
});
console.info(
@@ -110,7 +130,10 @@ export const ExtensionStoreStateProvider = ({
headers ? headers.length : 0
} extensions from the extension store.`
);
setExtensionShortHeadersByName(extensionShortHeadersByName);
setTranslatedExtensionShortHeadersByName(
translatedExtensionShortHeadersByName
);
setLoadedLanguage(language);
setFirstExtensionIds(extensionRegistry.views.default.firstIds);
} catch (error) {
console.error(
@@ -123,14 +146,24 @@ export const ExtensionStoreStateProvider = ({
isLoading.current = false;
})();
},
[extensionShortHeadersByName, isLoading]
[
translatedExtensionShortHeadersByName,
isLoading,
i18n,
language,
loadedLanguage,
]
);
React.useEffect(
() => {
// Don't attempt to load again extensions and filters if they
// were loaded already.
if (Object.keys(extensionShortHeadersByName).length || isLoading.current)
if (
(Object.keys(translatedExtensionShortHeadersByName).length &&
loadedLanguage === language) ||
isLoading.current
)
return;
const timeoutId = setTimeout(() => {
@@ -139,28 +172,36 @@ export const ExtensionStoreStateProvider = ({
}, EXTENSIONS_FETCH_TIMEOUT);
return () => clearTimeout(timeoutId);
},
[fetchExtensionsAndFilters, extensionShortHeadersByName, isLoading]
[
fetchExtensionsAndFilters,
translatedExtensionShortHeadersByName,
isLoading,
language,
loadedLanguage,
]
);
const allCategories = React.useMemo(
() => {
const categoriesSet = new Set();
for (const name in extensionShortHeadersByName) {
categoriesSet.add(extensionShortHeadersByName[name].category);
for (const name in translatedExtensionShortHeadersByName) {
categoriesSet.add(translatedExtensionShortHeadersByName[name].category);
}
const sortedCategories = [...categoriesSet].sort((tag1, tag2) =>
tag1.toLowerCase().localeCompare(tag2.toLowerCase())
);
return sortedCategories;
},
[extensionShortHeadersByName]
[translatedExtensionShortHeadersByName]
);
const filters = React.useMemo(
() => {
const tagsSet = new Set();
for (const name in extensionShortHeadersByName) {
extensionShortHeadersByName[name].tags.forEach(tag => tagsSet.add(tag));
for (const name in translatedExtensionShortHeadersByName) {
translatedExtensionShortHeadersByName[name].tags.forEach(tag =>
tagsSet.add(tag)
);
}
const sortedTags = [...tagsSet].sort((tag1, tag2) =>
tag1.toLowerCase().localeCompare(tag2.toLowerCase())
@@ -171,12 +212,12 @@ export const ExtensionStoreStateProvider = ({
tagsTree: [],
};
},
[extensionShortHeadersByName]
[translatedExtensionShortHeadersByName]
);
const searchResults: ?Array<
SearchResult<ExtensionShortHeader>
> = useSearchStructuredItem(extensionShortHeadersByName, {
> = useSearchStructuredItem(translatedExtensionShortHeadersByName, {
searchText,
chosenItemCategory: chosenCategory,
chosenCategory: filtersState.chosenCategory,
@@ -189,9 +230,9 @@ export const ExtensionStoreStateProvider = ({
const hasExtensionNamed = React.useCallback(
(extensionName: string) => {
return !!extensionShortHeadersByName[extensionName];
return !!translatedExtensionShortHeadersByName[extensionName];
},
[extensionShortHeadersByName]
[translatedExtensionShortHeadersByName]
);
const extensionStoreState = React.useMemo(
@@ -205,7 +246,7 @@ export const ExtensionStoreStateProvider = ({
error,
searchText,
setSearchText,
extensionShortHeadersByName,
translatedExtensionShortHeadersByName,
filtersState,
hasExtensionNamed,
}),
@@ -217,7 +258,7 @@ export const ExtensionStoreStateProvider = ({
chosenCategory,
setChosenCategory,
searchText,
extensionShortHeadersByName,
translatedExtensionShortHeadersByName,
filtersState,
fetchExtensionsAndFilters,
hasExtensionNamed,
@@ -101,6 +101,7 @@ export default function NewBehaviorDialog({
eventsFunctionsExtension
)
: [];
return behaviorMetadataList
.filter(behavior => !behavior.behaviorMetadata.isHidden())
.map(behavior => ({
@@ -7,6 +7,7 @@ import { Column } from '../../UI/Grid';
import Window from '../../Utils/Window';
import PreferencesContext from './PreferencesContext';
import LocalesMetadata from '../../locales/LocalesMetadata';
import ExtensionLocalesMetadata from '../../locales/ExtensionLocalesMetadata';
import Text from '../../UI/Text';
import Link from '../../UI/Link';
import { LineStackLayout } from '../../UI/Layout';
@@ -23,7 +24,27 @@ const displayLocaleMetadata = localeMetadata => {
return true;
};
const localesToDisplay = LocalesMetadata.filter(displayLocaleMetadata);
const localesToDisplay = LocalesMetadata.filter(displayLocaleMetadata).map(
localeMetadata => {
const extensionLocaleMetadata = ExtensionLocalesMetadata.find(
extensionLocaleMetadata =>
extensionLocaleMetadata.languageCode === localeMetadata.languageCode
);
const editorTranslationRatio = localeMetadata.translationRatio || 0;
// We do a simple 50/50 split between the main GDevelop locales and the extension locales.
// This is not perfect, but gives a rough idea of the translation progress.
const translationRatioExtension = extensionLocaleMetadata
? extensionLocaleMetadata.translationRatio
: 0;
const translationRatio =
(editorTranslationRatio + translationRatioExtension) / 2;
return {
...localeMetadata,
translationRatio,
};
}
);
const goodProgressLocales = localesToDisplay.filter(
localeMetadata => localeMetadata.translationRatio > 0.5
);
+6 -2
View File
@@ -110,8 +110,12 @@ const Providers = ({
<CourseChapterStoreStateProvider>
<ProductLicenseStoreStateProvider>
<MarketingPlansStoreStateProvider>
<ExtensionStoreStateProvider>
<BehaviorStoreStateProvider>
<ExtensionStoreStateProvider
i18n={i18n}
>
<BehaviorStoreStateProvider
i18n={i18n}
>
<TutorialStateProvider>
<AnnouncementsFeedStateProvider>
<PrivateAssetsAuthorizationProvider>
+4
View File
@@ -781,8 +781,12 @@ const MainFrame = (props: Props) => {
// dialog is closed after a language change. We then reload GDevelop
// extensions so that they declare all objects/actions/condition/etc...
// using the new language.
console.info('Language changed, reloading extensions...');
gd.MeasurementUnit.applyTranslation();
gd.JsPlatform.get().reloadBuiltinExtensions();
eventsFunctionsExtensionsState.reloadProjectEventsFunctionsExtensions(
currentProject
);
_loadExtensions().catch(() => {});
};
@@ -370,7 +370,7 @@ export const EventFunctionExtensionItem = ({
const name = eventsFunctionsExtension.getName();
const iconUrl = eventsFunctionsExtension.getIconUrl();
const { extensionShortHeadersByName } = React.useContext(
const { translatedExtensionShortHeadersByName } = React.useContext(
ExtensionStoreContext
);
@@ -393,7 +393,7 @@ export const EventFunctionExtensionItem = ({
}
primaryText={name}
editingName={isEditingName}
onEdit={() => onEdit(extensionShortHeadersByName)}
onEdit={() => onEdit(translatedExtensionShortHeadersByName)}
onDelete={onDelete}
addLabel={t`Add a New Extension`}
onAdd={onAdd}
+3 -3
View File
@@ -702,7 +702,7 @@ const ProjectManager = React.forwardRef<Props, ProjectManagerInterface>(
[editName, onProjectItemModified, scrollToItem]
);
const { extensionShortHeadersByName } = React.useContext(
const { translatedExtensionShortHeadersByName } = React.useContext(
ExtensionStoreContext
);
@@ -718,7 +718,7 @@ const ProjectManager = React.forwardRef<Props, ProjectManagerInterface>(
}
const originIdentifier = eventsFunctionsExtension.getOriginIdentifier();
const extensionShortHeader =
extensionShortHeadersByName[originIdentifier];
translatedExtensionShortHeadersByName[originIdentifier];
if (!extensionShortHeader) {
console.warn(
`This extension was downloaded from the store but its reference ${originIdentifier} couldn't be found in the store. Opening the extension in the editor...`
@@ -729,7 +729,7 @@ const ProjectManager = React.forwardRef<Props, ProjectManagerInterface>(
setOpenedExtensionShortHeader(extensionShortHeader);
setOpenedExtensionName(name);
},
[extensionShortHeadersByName, onOpenEventsFunctionsExtension]
[translatedExtensionShortHeadersByName, onOpenEventsFunctionsExtension]
);
const addExternalEvents = React.useCallback(
+42 -29
View File
@@ -39,41 +39,54 @@ export default class GDI18nProvider extends React.Component<Props, State> {
}
}
_loadCatalog = (language: string): Promise<Catalogs> => {
_loadCatalog = async (language: string): Promise<Catalogs> => {
if (this.state.catalogs[language]) {
return Promise.resolve(this.state.catalogs);
return this.state.catalogs;
}
return import(/* webpackMode: "lazy", webpackChunkName: "locales-[request]" */
`../../locales/${language.replace('-', '_')}/messages`).then(
catalog => {
return { ...this.state.catalogs, [language]: catalog };
},
(error: Error) => {
console.error('Error while loading language=' + language, error);
return this.state.catalogs;
}
);
try {
const [editorCatalog, extensionCatalog] = await Promise.all([
import(/* webpackMode: "lazy", webpackChunkName: "locales-[request]" */
`../../locales/${language.replace(
'-',
'_'
)}/messages` /* webpackMode: "lazy", webpackChunkName: "extension-locales-[request]" */),
import(`../../locales/${language.replace(
'-',
'_'
)}/extension-messages`),
]);
const catalog = {
languageData: editorCatalog.languageData,
messages: {
...extensionCatalog.messages,
...editorCatalog.messages,
},
};
return { ...this.state.catalogs, [language]: catalog };
} catch (error) {
console.error('Error while loading language=' + language, error);
return this.state.catalogs;
}
};
_loadLanguage(language: string) {
this._loadCatalog(language).then(catalogs => {
this.setState(
{
language,
async _loadLanguage(language: string) {
const catalogs = await this._loadCatalog(language);
this.setState(
{
language,
catalogs,
i18n: setupI18n({
language: language,
catalogs,
i18n: setupI18n({
language: language,
catalogs,
}),
},
() => {
const { i18n } = this.state;
gd.getTranslation = getTranslationFunction(i18n);
console.info(`Loaded "${language}" language`);
}
);
});
}),
},
() => {
const { i18n } = this.state;
gd.getTranslation = getTranslationFunction(i18n);
console.info(`Loaded "${language}" language`);
}
);
}
render() {
@@ -0,0 +1,352 @@
// This file is generated by compile-translations.js script
// prettier-ignore
module.exports = [
{
"languageCode": "af_ZA",
"languageName": "Afrikaans",
"languageNativeName": "Afrikaans",
"translationRatio": 0
},
{
"languageCode": "ar_SA",
"languageName": "Arabic",
"languageNativeName": "اَلْعَرَبِيَّةُ",
"translationRatio": 0.9996659986639946
},
{
"languageCode": "az_AZ",
"languageName": "Azerbaijani",
"languageNativeName": "azərbaycan dili",
"translationRatio": 0
},
{
"languageCode": "bg_BG",
"languageName": "Bulgarian",
"languageNativeName": "български език",
"translationRatio": 0
},
{
"languageCode": "bn_BD",
"languageName": "Bengali",
"languageNativeName": "বাংলা",
"translationRatio": 0
},
{
"languageCode": "br_FR",
"languageName": "Breton",
"languageNativeName": "brezhoneg",
"translationRatio": 0
},
{
"languageCode": "ca_ES",
"languageName": "Catalan",
"languageNativeName": "Català",
"translationRatio": 0
},
{
"languageCode": "cs_CZ",
"languageName": "Czech",
"languageNativeName": "čeština",
"translationRatio": 0
},
{
"languageCode": "da_DK",
"languageName": "Danish",
"languageNativeName": "Dansk",
"translationRatio": 0
},
{
"languageCode": "de_DE",
"languageName": "German",
"languageNativeName": "Deutsch",
"translationRatio": 0.9931529726118904
},
{
"languageCode": "el_GR",
"languageName": "Greek",
"languageNativeName": "Ελληνικά",
"translationRatio": 0
},
{
"languageCode": "en",
"languageName": "English",
"languageNativeName": "English",
"translationRatio": 0
},
{
"languageCode": "eo_UY",
"languageName": "Esperanto",
"languageNativeName": "Esperanto",
"translationRatio": 0
},
{
"languageCode": "es_ES",
"languageName": "Spanish",
"languageNativeName": "Español",
"translationRatio": 0.9953239812959251
},
{
"languageCode": "fa_IR",
"languageName": "Persian",
"languageNativeName": "فارسی",
"translationRatio": 0
},
{
"languageCode": "fi_FI",
"languageName": "Finnish",
"languageNativeName": "suomi",
"translationRatio": 0.9974949899799599
},
{
"languageCode": "fil_PH",
"languageName": "Filipino",
"languageNativeName": "Mga Filipino",
"translationRatio": 0
},
{
"languageCode": "fr_FR",
"languageName": "French",
"languageNativeName": "Français",
"translationRatio": 0.9919839679358717
},
{
"languageCode": "ha_HG",
"languageName": "Hausa",
"languageNativeName": "هَوُسَ",
"translationRatio": 0
},
{
"languageCode": "he_IL",
"languageName": "Hebrew",
"languageNativeName": "עברית",
"translationRatio": 0
},
{
"languageCode": "hi_IN",
"languageName": "Hindi",
"languageNativeName": "हिन्दी",
"translationRatio": 0
},
{
"languageCode": "hu_HU",
"languageName": "Hungarian",
"languageNativeName": "magyar",
"translationRatio": 0
},
{
"languageCode": "id_ID",
"languageName": "Indonesian",
"languageNativeName": "Bahasa Indonesia",
"translationRatio": 0
},
{
"languageCode": "ig_NG",
"languageName": "Igbo",
"languageNativeName": "Asụsụ Igbo",
"translationRatio": 0
},
{
"languageCode": "it_IT",
"languageName": "Italian",
"languageNativeName": "Italiano",
"translationRatio": 0.9964929859719439
},
{
"languageCode": "ja_JP",
"languageName": "Japanese",
"languageNativeName": "日本語",
"translationRatio": 0.5930193720774883
},
{
"languageCode": "ka_GE",
"languageName": "Georgian",
"languageNativeName": "ქართული",
"translationRatio": 0
},
{
"languageCode": "km_KH",
"languageName": "Khmer",
"languageNativeName": "ខេមរភាសា",
"translationRatio": 0
},
{
"languageCode": "ko_KR",
"languageName": "Korean",
"languageNativeName": "한국어",
"translationRatio": 0.970440881763527
},
{
"languageCode": "lt_LT",
"languageName": "Lithuanian",
"languageNativeName": "lietuvių kalba",
"translationRatio": 0
},
{
"languageCode": "lv_LV",
"languageName": "Latvian",
"languageNativeName": "latviešu valoda",
"translationRatio": 0
},
{
"languageCode": "mr_IN",
"languageName": "Marathi",
"languageNativeName": "मराठी",
"translationRatio": 0
},
{
"languageCode": "ms_MY",
"languageName": "Malay",
"languageNativeName": "Bahasa Melayu",
"translationRatio": 0
},
{
"languageCode": "my_MM",
"languageName": "Burmese",
"languageNativeName": "ဗမာစာ",
"translationRatio": 0
},
{
"languageCode": "nl_NL",
"languageName": "Dutch",
"languageNativeName": "Nederlands",
"translationRatio": 0.9851369405477622
},
{
"languageCode": "no_NO",
"languageName": "Norwegian",
"languageNativeName": "Norsk",
"translationRatio": 0
},
{
"languageCode": "pl_PL",
"languageName": "Polish",
"languageNativeName": "Polski",
"translationRatio": 0.9961589846359385
},
{
"languageCode": "pt_BR",
"languageName": "Brazilian Portuguese",
"languageNativeName": "Português brasileiro",
"translationRatio": 0.9953239812959251
},
{
"languageCode": "pt_PT",
"languageName": "Portuguese",
"languageNativeName": "Português",
"translationRatio": 0
},
{
"languageCode": "ro_RO",
"languageName": "Romanian",
"languageNativeName": "Română",
"translationRatio": 0
},
{
"languageCode": "ru_RU",
"languageName": "Russian",
"languageNativeName": "Русский",
"translationRatio": 0.9978289913159653
},
{
"languageCode": "si_LK",
"languageName": "Sinhala",
"languageNativeName": "සිංහල",
"translationRatio": 0
},
{
"languageCode": "sk_SK",
"languageName": "Slovak",
"languageNativeName": "slovenčina",
"translationRatio": 0
},
{
"languageCode": "sl_SI",
"languageName": "Slovenian",
"languageNativeName": "slovenščina",
"translationRatio": 0
},
{
"languageCode": "sq_AL",
"languageName": "Albanian",
"languageNativeName": "Shqip",
"translationRatio": 0
},
{
"languageCode": "sr_CS",
"languageName": "Serbian (Latin)",
"languageNativeName": "srpski",
"translationRatio": 0
},
{
"languageCode": "sr_SP",
"languageName": "Serbian",
"languageNativeName": "српски језик",
"translationRatio": 0
},
{
"languageCode": "sv_SE",
"languageName": "Swedish",
"languageNativeName": "Svenska",
"translationRatio": 0
},
{
"languageCode": "sw_KE",
"languageName": "Swahili",
"languageNativeName": "Kiswahili",
"translationRatio": 0
},
{
"languageCode": "th_TH",
"languageName": "Thai",
"languageNativeName": "ไทย",
"translationRatio": 0
},
{
"languageCode": "tr_TR",
"languageName": "Turkish",
"languageNativeName": "Türkçe",
"translationRatio": 0.9956579826319305
},
{
"languageCode": "uk_UA",
"languageName": "Ukrainian",
"languageNativeName": "Українська",
"translationRatio": 0.9979959919839679
},
{
"languageCode": "ur_PK",
"languageName": "Urdu",
"languageNativeName": "اردو",
"translationRatio": 0
},
{
"languageCode": "uz_UZ",
"languageName": "Uzbek",
"languageNativeName": "Ўзбек",
"translationRatio": 0
},
{
"languageCode": "vi_VN",
"languageName": "Vietnamese",
"languageNativeName": "Tiếng Việt",
"translationRatio": 0
},
{
"languageCode": "yo_NG",
"languageName": "Yoruba",
"languageNativeName": "Yorùbá",
"translationRatio": 0
},
{
"languageCode": "zh_CN",
"languageName": "Chinese Simplified",
"languageNativeName": "简化字",
"translationRatio": 0.9946559786239145
},
{
"languageCode": "zh_TW",
"languageName": "Chinese Traditional",
"languageNativeName": "正體字",
"translationRatio": 0.9128256513026052
}
];
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,9 @@
/* eslint-disable */ module.exports = {
languageData: {
plurals: function(n, ord) {
if (ord) return n == 1 ? 'one' : 'other';
return n >= 0 && n < 2 ? 'one' : 'other';
},
},
messages: {},
};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,6 @@
// @flow
import * as React from 'react';
import { I18n } from '@lingui/react';
import { action } from '@storybook/addon-actions';
import paperDecorator from '../../../PaperDecorator';
@@ -43,16 +44,20 @@ const apiDataFakeExtensions = {
};
export const Default = () => (
<FixedHeightFlexContainer height={400}>
<ExtensionStoreStateProvider>
<ExtensionStore
project={testProject.project}
isInstalling={false}
onInstall={action('onInstall')}
showOnlyWithBehaviors={false}
/>
</ExtensionStoreStateProvider>
</FixedHeightFlexContainer>
<I18n>
{({ i18n }) => (
<FixedHeightFlexContainer height={400}>
<ExtensionStoreStateProvider i18n={i18n}>
<ExtensionStore
project={testProject.project}
isInstalling={false}
onInstall={action('onInstall')}
showOnlyWithBehaviors={false}
/>
</ExtensionStoreStateProvider>
</FixedHeightFlexContainer>
)}
</I18n>
);
Default.parameters = apiDataFakeExtensions;
@@ -67,9 +72,31 @@ export const WithCommunityExtensions = () => {
};
return (
<PreferencesContext.Provider value={preferences}>
<I18n>
{({ i18n }) => (
<PreferencesContext.Provider value={preferences}>
<FixedHeightFlexContainer height={400}>
<ExtensionStoreStateProvider i18n={i18n}>
<ExtensionStore
project={testProject.project}
isInstalling={false}
onInstall={action('onInstall')}
showOnlyWithBehaviors={false}
/>
</ExtensionStoreStateProvider>
</FixedHeightFlexContainer>
</PreferencesContext.Provider>
)}
</I18n>
);
};
WithCommunityExtensions.parameters = apiDataFakeExtensions;
export const WithServerSideErrors = () => (
<I18n>
{({ i18n }) => (
<FixedHeightFlexContainer height={400}>
<ExtensionStoreStateProvider>
<ExtensionStoreStateProvider i18n={i18n}>
<ExtensionStore
project={testProject.project}
isInstalling={false}
@@ -78,63 +105,64 @@ export const WithCommunityExtensions = () => {
/>
</ExtensionStoreStateProvider>
</FixedHeightFlexContainer>
</PreferencesContext.Provider>
);
};
WithCommunityExtensions.parameters = apiDataFakeExtensions;
export const WithServerSideErrors = () => (
<FixedHeightFlexContainer height={400}>
<ExtensionStoreStateProvider>
<ExtensionStore
project={testProject.project}
isInstalling={false}
onInstall={action('onInstall')}
showOnlyWithBehaviors={false}
/>
</ExtensionStoreStateProvider>
</FixedHeightFlexContainer>
)}
</I18n>
);
WithServerSideErrors.parameters = apiDataServerSideError;
export const ShowingAnAlreadyInstalledExtension = () => (
<FixedHeightFlexContainer height={400}>
<ExtensionStoreStateProvider defaultSearchText="Fake installed">
<ExtensionStore
project={testProject.project}
isInstalling={false}
onInstall={action('onInstall')}
showOnlyWithBehaviors={false}
/>
</ExtensionStoreStateProvider>
</FixedHeightFlexContainer>
<I18n>
{({ i18n }) => (
<FixedHeightFlexContainer height={400}>
<ExtensionStoreStateProvider
i18n={i18n}
defaultSearchText="Fake installed"
>
<ExtensionStore
project={testProject.project}
isInstalling={false}
onInstall={action('onInstall')}
showOnlyWithBehaviors={false}
/>
</ExtensionStoreStateProvider>
</FixedHeightFlexContainer>
)}
</I18n>
);
ShowingAnAlreadyInstalledExtension.parameters = apiDataFakeExtensions;
export const ExtensionBeingInstalled = () => (
<FixedHeightFlexContainer height={400}>
<ExtensionStoreStateProvider>
<ExtensionStore
project={testProject.project}
isInstalling={true}
onInstall={action('onInstall')}
showOnlyWithBehaviors={false}
/>
</ExtensionStoreStateProvider>
</FixedHeightFlexContainer>
<I18n>
{({ i18n }) => (
<FixedHeightFlexContainer height={400}>
<ExtensionStoreStateProvider i18n={i18n}>
<ExtensionStore
project={testProject.project}
isInstalling={true}
onInstall={action('onInstall')}
showOnlyWithBehaviors={false}
/>
</ExtensionStoreStateProvider>
</FixedHeightFlexContainer>
)}
</I18n>
);
ExtensionBeingInstalled.parameters = apiDataFakeExtensions;
export const OnlyWithBehaviors = () => (
<FixedHeightFlexContainer height={400}>
<ExtensionStoreStateProvider>
<ExtensionStore
project={testProject.project}
isInstalling={false}
onInstall={action('onInstall')}
showOnlyWithBehaviors={true}
/>
</ExtensionStoreStateProvider>
</FixedHeightFlexContainer>
<I18n>
{({ i18n }) => (
<FixedHeightFlexContainer height={400}>
<ExtensionStoreStateProvider i18n={i18n}>
<ExtensionStore
project={testProject.project}
isInstalling={false}
onInstall={action('onInstall')}
showOnlyWithBehaviors={true}
/>
</ExtensionStoreStateProvider>
</FixedHeightFlexContainer>
)}
</I18n>
);
OnlyWithBehaviors.parameters = apiDataFakeExtensions;
@@ -48,7 +48,7 @@ export const Default = () => (
eventsFunctionsExtensionWriter={null}
eventsFunctionsExtensionOpener={null}
>
<ExtensionStoreStateProvider>
<ExtensionStoreStateProvider i18n={i18n}>
<ExtensionsSearchDialog
project={testProject.project}
onClose={action('onClose')}
@@ -72,7 +72,7 @@ export const WithServerSideError = () => (
eventsFunctionsExtensionWriter={null}
eventsFunctionsExtensionOpener={null}
>
<ExtensionStoreStateProvider>
<ExtensionStoreStateProvider i18n={i18n}>
<ExtensionsSearchDialog
project={testProject.project}
onClose={action('onClose')}
@@ -2,6 +2,7 @@
import * as React from 'react';
import { action } from '@storybook/addon-actions';
import { I18n } from '@lingui/react';
// Keep first as it creates the `global.gd` object:
import { testProject } from '../../GDevelopJsInitializerDecorator';
@@ -44,21 +45,25 @@ const apiDataFakeBehaviors = {
};
export const DefaultForSpriteObject = () => (
<BehaviorStoreStateProvider>
<NewBehaviorDialog
open
project={testProject.project}
eventsFunctionsExtension={null}
objectType={'Sprite'}
onClose={action('on close')}
onChoose={action('on choose')}
objectBehaviorsTypes={[
'DestroyOutsideBehavior::DestroyOutside',
'PlatformBehavior::PlatformBehavior',
]}
onExtensionInstalled={action('extension installed')}
/>
</BehaviorStoreStateProvider>
<I18n>
{({ i18n }) => (
<BehaviorStoreStateProvider i18n={i18n}>
<NewBehaviorDialog
open
project={testProject.project}
eventsFunctionsExtension={null}
objectType={'Sprite'}
onClose={action('on close')}
onChoose={action('on choose')}
objectBehaviorsTypes={[
'DestroyOutsideBehavior::DestroyOutside',
'PlatformBehavior::PlatformBehavior',
]}
onExtensionInstalled={action('extension installed')}
/>
</BehaviorStoreStateProvider>
)}
</I18n>
);
DefaultForSpriteObject.parameters = apiDataFakeBehaviors;
@@ -73,9 +78,38 @@ export const WithCommunityExtensions = () => {
};
return (
<PreferencesContext.Provider value={preferences}>
<I18n>
{({ i18n }) => (
<PreferencesContext.Provider value={preferences}>
<FixedHeightFlexContainer height={400}>
<BehaviorStoreStateProvider i18n={i18n}>
<NewBehaviorDialog
open
project={testProject.project}
eventsFunctionsExtension={null}
objectType={'Sprite'}
onClose={action('on close')}
onChoose={action('on choose')}
objectBehaviorsTypes={[
'DestroyOutsideBehavior::DestroyOutside',
'PlatformBehavior::PlatformBehavior',
]}
onExtensionInstalled={action('extension installed')}
/>
</BehaviorStoreStateProvider>
</FixedHeightFlexContainer>
</PreferencesContext.Provider>
)}
</I18n>
);
};
WithCommunityExtensions.parameters = apiDataFakeBehaviors;
export const WithServerSideErrors = () => (
<I18n>
{({ i18n }) => (
<FixedHeightFlexContainer height={400}>
<BehaviorStoreStateProvider>
<BehaviorStoreStateProvider i18n={i18n}>
<NewBehaviorDialog
open
project={testProject.project}
@@ -91,28 +125,7 @@ export const WithCommunityExtensions = () => {
/>
</BehaviorStoreStateProvider>
</FixedHeightFlexContainer>
</PreferencesContext.Provider>
);
};
WithCommunityExtensions.parameters = apiDataFakeBehaviors;
export const WithServerSideErrors = () => (
<FixedHeightFlexContainer height={400}>
<BehaviorStoreStateProvider>
<NewBehaviorDialog
open
project={testProject.project}
eventsFunctionsExtension={null}
objectType={'Sprite'}
onClose={action('on close')}
onChoose={action('on choose')}
objectBehaviorsTypes={[
'DestroyOutsideBehavior::DestroyOutside',
'PlatformBehavior::PlatformBehavior',
]}
onExtensionInstalled={action('extension installed')}
/>
</BehaviorStoreStateProvider>
</FixedHeightFlexContainer>
)}
</I18n>
);
WithServerSideErrors.parameters = apiDataServerSideError;