I faced this problem recently, so I will share what I tried and what it worked for me. Note that I needed to implement a change-font action at runtime as well.
In a Swing application, we usually expand container classes for the core containers. Let's say we want to create Facebook for desktop. Someone could create 3 core classes (extending JPanel or JScrollPane). Let's say: LeftPanel extends JPanel, MiddlePanel extends JPanel and RightPanel extends JPanel. Left panel will represent the left menu, middle panel will represent the main scrolling view and finally the right panel will represent the ads area. Of course, every single of this panels will have inherited JPanels (and probably some of them will have a new class as well, e.g PostPanel, CommentSectionPanel etc.)
Now, assuming you have read The Use of Multiple JFrames: Good or Bad Practice?, your application uses only one JFrame and it hosts every single component in it. Even modal JDialogs are based on it (have it as its parent). So, you can keep it somewhere like a Singleton.
In order to change components text, we will have to call setText for each one of them. Calling JFrame's getComponents will give us all its components. If one of them is container, we will have to call getComponents for it, since it probably has components in it as well. The solution is a recursion to find all of them:
private static <T extends Component> List<T> getChildren(Class<T> clazz, final Container container) {
    Component[] components;
    if (container instanceof JMenu)
        components = ((JMenu) container).getMenuComponents();
    else
        components = container.getComponents();
    List<T> compList = new ArrayList<T>();
    for (Component comp : components) {
        if (clazz.isAssignableFrom(comp.getClass())) {
            compList.add(clazz.cast(comp));
        }
        if (comp instanceof Container)
            compList.addAll(getChildren(clazz, (Container) comp));
    }
    return compList;
}
Calling this method with arguments java.awt.Component.class and myJFrame will give you all the components your JFrame has.
Now, we have to separate which of them can be "refreshed" (change language) and which of them can't. Let's create an Interface for that:
public static interface LocaleChangeable {
    void localeChanged(Locale newLocale);
}
Now instead of giving this ability to each one of them, we give it to the big containers (the example with facebook):
LeftPanel extends JPanel implements LocaleChangeable, RightPanel extends JPanel implements LocaleChangeable because they have components with text property.
These classes now are responsible for changing the text of their components. A pseduo-example would be:
public class LeftPanel extends JPanel implements LocaleChangeable {
    private JLabel exitLabel;
    @Override
    public void localeChanged(Locale newLocale) {
        if (newLocale == Locale.ENGLISH) {
            exitLabel.setText("Exit");
        } else if (newLocale == Locale.GREEK) {
            exitLabel.setText("Έξοδος"); //Greek exit
        }
    }
}
(Of course instead of a bunch of if-else conditions, the ResourceBundle logic will take place).
So... Let's call this method for all classes-containers:
private void broadcastLocaleChange(Locale locale) {
    List<Component> components = getChildren(Component.class, myFrame);
    components.stream().filter(LocaleChangeable.class::isInstance).map(LocaleChangeable.class::cast)
            .forEach(lc -> lc.localeChanged(locale));
}
That's it! A full example would be:
public class LocaleTest extends JFrame {
    private static final long serialVersionUID = 1L;
    public LocaleTest() {
        super("test");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        getContentPane().setLayout(new BorderLayout());
        add(new MainPanel());
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }
    private class MainPanel extends JPanel implements LocaleChangeable {
        private JLabel label;
        private JButton changeLocaleButton;
        public MainPanel() {
            super(new FlowLayout());
            label = new JLabel(Locale.ENGLISH.toString());
            add(label);
            changeLocaleButton = new JButton("Change Locale");
            changeLocaleButton.addActionListener(e -> {
                broadcastLocaleChange(Locale.CANADA);
            });
            add(changeLocaleButton);
        }
        @Override
        public void localeChanged(Locale newLocale) {
            label.setText(newLocale.toString());
            System.out.println("Language changed.");
        }
        private void broadcastLocaleChange(Locale locale) {
            List<Component> components = getChildren(Component.class, LocaleTest.this);
            components.stream().filter(LocaleChangeable.class::isInstance).map(LocaleChangeable.class::cast)
                    .forEach(lc -> lc.localeChanged(locale));
        }
    }
    private static <T extends Component> List<T> getChildren(Class<T> clazz, final Container container) {
        Component[] components;
        if (container instanceof JMenu)
            components = ((JMenu) container).getMenuComponents();
        else
            components = container.getComponents();
        List<T> compList = new ArrayList<T>();
        for (Component comp : components) {
            if (clazz.isAssignableFrom(comp.getClass())) {
                compList.add(clazz.cast(comp));
            }
            if (comp instanceof Container)
                compList.addAll(getChildren(clazz, (Container) comp));
        }
        return compList;
    }
    public static interface LocaleChangeable {
        void localeChanged(Locale newLocale);
    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new LocaleTest().setVisible(true));
    }
}