mirror of
https://github.com/voltbonn/profile-picture-generator.git
synced 2024-12-22 07:45:04 +00:00
started localization
This commit is contained in:
parent
a5f000b96f
commit
d31f09cc30
7 changed files with 188 additions and 2 deletions
|
@ -4,11 +4,15 @@
|
|||
"private": true,
|
||||
"homepage": "http://profile_generator.volt-bonn.de/",
|
||||
"dependencies": {
|
||||
"@fluent/bundle": "^0.16.0",
|
||||
"@fluent/langneg": "^0.5.0",
|
||||
"@fluent/react": "^0.13.0",
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"hammerjs": "^2.0.8",
|
||||
"hamsterjs": "^1.1.3",
|
||||
"intl-pluralrules": "^1.2.2",
|
||||
"merge-images": "^2.0.0",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
|
|
10
src/App.js
10
src/App.js
|
@ -9,6 +9,12 @@ import HeaderImage from './HeaderImage.svg'
|
|||
import purpleBG from './purpleBG.png'
|
||||
import empty_1x1 from './empty_1x1.png'
|
||||
|
||||
import 'intl-pluralrules'
|
||||
import { AppLocalizationProvider } from './l10n.js'
|
||||
import { Localized } from './Localized.js'
|
||||
|
||||
// const userLocales = ['de'] || navigator.languages
|
||||
const userLocales = navigator.languages
|
||||
|
||||
const frameSize = 1080
|
||||
|
||||
|
@ -279,6 +285,7 @@ function App() {
|
|||
|
||||
|
||||
return (
|
||||
<AppLocalizationProvider key="AppLocalizationProvider" userLocales={userLocales}>
|
||||
<div className="App" {...getRootProps()}>
|
||||
<img src={HeaderImage} className="HeaderImage" alt="Volt Logo" />
|
||||
|
||||
|
@ -286,7 +293,7 @@ function App() {
|
|||
Drop your photo here ...
|
||||
</div>
|
||||
|
||||
<h2>Choose your Photo:</h2>
|
||||
<h2><Localized id="choose_your_photo" /></h2>
|
||||
<p>It should best be a square image or your face in the middle. The photo is not saved and never leaves your computer.</p>
|
||||
|
||||
<label className="labelButton" tabIndex="0" style={{outline:'none'}}>
|
||||
|
@ -326,6 +333,7 @@ function App() {
|
|||
<a href="https://github.com/voltbonn/profile-picture-generator">Source Code</a>
|
||||
</footer>
|
||||
</div>
|
||||
</AppLocalizationProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
55
src/Localized.js
Normal file
55
src/Localized.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
Localized as LocalizedOriginal,
|
||||
// withLocalization,
|
||||
} from '@fluent/react'
|
||||
|
||||
import { FluentContext } from '../node_modules/@fluent/react/esm/context.js'
|
||||
|
||||
const Localized = props => (
|
||||
<LocalizedOriginal
|
||||
key={props.id}
|
||||
{...props}
|
||||
elems={{
|
||||
br: <br />,
|
||||
...props.elems,
|
||||
}}
|
||||
>
|
||||
<React.Fragment>{props.children}</React.Fragment>
|
||||
</LocalizedOriginal>
|
||||
)
|
||||
|
||||
// A custom withLocalization to have an empty fallback.
|
||||
// It is nearly identical to the original.
|
||||
function withLocalization(Inner) {
|
||||
function WithLocalization(props) {
|
||||
const l10n = React.useContext(FluentContext)
|
||||
|
||||
const getString = (id, args, fallback) => l10n.getString(id, args, fallback || ' ')
|
||||
|
||||
return React.createElement(Inner, { getString, ...props })
|
||||
}
|
||||
return WithLocalization
|
||||
}
|
||||
|
||||
export {
|
||||
withLocalization,
|
||||
Localized,
|
||||
Localized as default,
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
import { Localized, withLocalization } from '../Localized/'
|
||||
|
||||
<Localized id="translation_id" />
|
||||
export default withLocalization(componentName)
|
||||
|
||||
|
||||
import Localized from '../Localized/'
|
||||
<Localized id="translation_id" />
|
||||
|
||||
import { withLocalization } from '@fluent/react'
|
||||
export default withLocalization(componentName)
|
||||
|
||||
*/
|
83
src/l10n.js
Normal file
83
src/l10n.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
import React from 'react'
|
||||
|
||||
// https://projectfluent.org/play/
|
||||
|
||||
// import {LocalizationProvider,Localized} from '@fluent/react' // '@fluent/react/compat'
|
||||
import { ReactLocalization, LocalizationProvider } from '@fluent/react'
|
||||
import { FluentBundle, FluentResource } from '@fluent/bundle'
|
||||
import { negotiateLanguages } from '@fluent/langneg'
|
||||
|
||||
const _supportedLocales_ = [
|
||||
'de',
|
||||
'en',
|
||||
]
|
||||
const _defaultLocale_ = 'en'
|
||||
|
||||
|
||||
async function fetchMessages(locale) {
|
||||
const path = await import('./locales/' + locale + '.ftl')
|
||||
|
||||
const response = await fetch(path.default)
|
||||
const messages = await response.text()
|
||||
|
||||
return { [locale]: new FluentResource(messages) }
|
||||
}
|
||||
|
||||
function getDefaultBundles() {
|
||||
const bundle = new FluentBundle('')
|
||||
bundle.addResource(new FluentResource(''))
|
||||
return new ReactLocalization([bundle])
|
||||
}
|
||||
|
||||
async function createMessagesGenerator(currentLocales) {
|
||||
const fetched = await Promise.all(
|
||||
currentLocales.map(fetchMessages)
|
||||
)
|
||||
const messages = fetched.reduce(
|
||||
(obj, cur) => Object.assign(obj, cur)
|
||||
)
|
||||
|
||||
return function* generateBundles() {
|
||||
for (const locale of currentLocales) {
|
||||
const bundle = new FluentBundle(locale)
|
||||
bundle.addResource(messages[locale])
|
||||
yield bundle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class AppLocalizationProvider extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
bundles: getDefaultBundles(),
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const currentLocales = negotiateLanguages(
|
||||
this.props.userLocales,
|
||||
_supportedLocales_,
|
||||
{ defaultLocale: _defaultLocale_ }
|
||||
)
|
||||
|
||||
const generateBundles = await createMessagesGenerator(currentLocales)
|
||||
this.setState({ bundles: new ReactLocalization(generateBundles()) })
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children } = this.props
|
||||
const { bundles } = this.state
|
||||
|
||||
if (!bundles) {
|
||||
// Show a loader.
|
||||
return <div>Loading texts…</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<LocalizationProvider l10n={bundles}>
|
||||
{children}
|
||||
</LocalizationProvider>
|
||||
)
|
||||
}
|
||||
}
|
1
src/locales/de.ftl
Normal file
1
src/locales/de.ftl
Normal file
|
@ -0,0 +1 @@
|
|||
choose_your_photo = Wähl dein Bild:
|
1
src/locales/en.ftl
Normal file
1
src/locales/en.ftl
Normal file
|
@ -0,0 +1 @@
|
|||
choose_your_photo = Choose your Photo:
|
36
yarn.lock
36
yarn.lock
|
@ -1164,6 +1164,30 @@
|
|||
minimatch "^3.0.4"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@fluent/bundle@^0.16.0":
|
||||
version "0.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@fluent/bundle/-/bundle-0.16.0.tgz#e0cab75ba6ce9d9147ace3cb6ec61fd90f31ec1f"
|
||||
integrity sha512-kUEAUePhb/y2BCcNpKOnjCs+WJkDczVpUUAQ+cDl0xvBGqL0Kv0Yog2oHSuv/Ou22c6KdXbvfCl3We0bIZnrmg==
|
||||
|
||||
"@fluent/langneg@^0.5.0":
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@fluent/langneg/-/langneg-0.5.0.tgz#de448070efa16c8fb6cc4af1629663f01e10689b"
|
||||
integrity sha512-jv0g3YO5byz29HXEE6DBzAog60q726mwV2nIoekEX590JVh+mbd6/ZXT5/l4mN2BMlrelzyscCTffKI4XScVtg==
|
||||
|
||||
"@fluent/react@^0.13.0":
|
||||
version "0.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@fluent/react/-/react-0.13.0.tgz#cf2652d56fe22072dfeb30dcd2e03650fb913f88"
|
||||
integrity sha512-NdITFI7eqecpb2Ty+I9g2BKxbzhdBKoKm8eU3/vnmBhGDeFgkS/aACSHF3gV2rL87JW9A+h4Ih1ympWp8OqqxQ==
|
||||
dependencies:
|
||||
"@fluent/sequence" "0.5.0"
|
||||
cached-iterable "^0.2.1"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
"@fluent/sequence@0.5.0":
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@fluent/sequence/-/sequence-0.5.0.tgz#6349b614711df2d8ed256598fa31a757fe032539"
|
||||
integrity sha512-70LhnbPO/sO2rI2vHws66QwKq9a7PKiEN0hrc65xY3LzWq3AlATZnt+EmFG1ihmPI+5mqGPnJMAdpp3qze3X2Q==
|
||||
|
||||
"@hapi/address@2.x.x":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||
|
@ -2989,6 +3013,11 @@ cache-base@^1.0.1:
|
|||
union-value "^1.0.0"
|
||||
unset-value "^1.0.0"
|
||||
|
||||
cached-iterable@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/cached-iterable/-/cached-iterable-0.2.1.tgz#723958f5e7adc74c96bedb10b426bdfd95f2fe6d"
|
||||
integrity sha512-8zAVjMjdn/S/QXJaOnqsko0+ZJzXT2Dum2u9TMGg5YR9fxONPrUjuO9VYqnb1AoldXeYVAcNJLgT5Q8WaIJSgA==
|
||||
|
||||
call-bind@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce"
|
||||
|
@ -5756,6 +5785,11 @@ internal-slot@^1.0.2:
|
|||
has "^1.0.3"
|
||||
side-channel "^1.0.2"
|
||||
|
||||
intl-pluralrules@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/intl-pluralrules/-/intl-pluralrules-1.2.2.tgz#2b73542a9502a8a3a742cdd917f3d969fb5482fe"
|
||||
integrity sha512-SBdlNCJAhTA0I0uHg2dn7I+c6BCvSVk6zJ/01ozjwJK7BvKms9RH3w3Sd/Ag24KffZ/Yx6KJRCKAc7eE8TZLNg==
|
||||
|
||||
ip-regex@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
|
||||
|
@ -8754,7 +8788,7 @@ prompts@2.4.0, prompts@^2.0.1:
|
|||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.5"
|
||||
|
||||
prop-types@^15.7.2:
|
||||
prop-types@^15.6.0, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
|
|
Loading…
Reference in a new issue